package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.PointsBoardQuery;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.TableNameContext;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.validation.constraints.Min;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.common.constants.Constant.POINTS_BOARD_TABLE_NAME;
import static com.tianji.common.utils.DateUtils.DATE_SUFFIX_FORMATTER;
import static com.tianji.learning.constants.RedisConstants.POINTS_BOARD_KEY_PREFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author WFan
 * @since 2025-04-02
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {
    private final RedisTemplate redisTemplate;
    private final UserClient userClient;

    /**
     * 分页查询指定赛季的积分排行榜
     *
     * @param query
     * @return
     */
    @Override
    public PointsBoardVO queryBoard(PointsBoardQuery query) {
        // 1.判断是否查询当前赛季信息
        Long season = query.getSeason();
        boolean isCurrentSeason = ObjectUtil.isEmpty(season) || season == 0;

        // 2.查询指定赛季我的积分信息
        String key = POINTS_BOARD_KEY_PREFIX + LocalDateTime.now().format(DATE_SUFFIX_FORMATTER);

        PointsBoard myBoard = isCurrentSeason ? queryMyCurrentBoard(key) : queryMyHistoryBoard(season);

        // 3.查询指定赛季的榜单列表信息
        List<PointsBoard> boards = isCurrentSeason ? queryCurrentBoards(key, query.getPageNo(), query.getPageSize()) : queryHistoryBoards(query);

        // 4.封装vo并返回
        PointsBoardVO pointsBoardVO = new PointsBoardVO();
        pointsBoardVO.setRank(myBoard.getRank());
        pointsBoardVO.setPoints(myBoard.getPoints());

        if (ObjectUtil.isEmpty(boards)){
            return pointsBoardVO;
        }

        // 5.获取userId集合
        Set<Long> userIds = boards.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        Map<Long, String> userNameMap = new HashMap<>(userDTOS.size());
        if (ObjectUtil.isNotEmpty(userDTOS)){
            userNameMap = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, r -> r.getName()));
            List<PointsBoardItemVO> pointsBoardItemVOList = new ArrayList<>(boards.size());
            for (PointsBoard board : boards) {
                PointsBoardItemVO vo = new PointsBoardItemVO();
                vo.setName(userNameMap.get(board.getUserId()));
                vo.setRank(board.getRank());
                vo.setPoints(board.getPoints());
                pointsBoardItemVOList.add(vo);
            }
            pointsBoardVO.setBoardList(pointsBoardItemVOList);
        }
        return pointsBoardVO;
    }

    /**
     * 根据赛季ID动态创建表
     *
     * @param seasonId
     */
    @Override
    public void createTable(Integer seasonId) {
        this.getBaseMapper().createTable(POINTS_BOARD_TABLE_NAME + "_" + seasonId);
    }

    /**
     * 查询历史赛季榜单信息 TODO
     *
     * @param query
     * @return
     */
    private List<PointsBoard> queryHistoryBoards(PointsBoardQuery query) {
        // 1.组装表名
        String tableName = POINTS_BOARD_TABLE_NAME + "_" + query.getSeason();
        TableNameContext.setTableName(tableName);

        // 2.查询榜单
        Page<PointsBoard> boardPage = this.lambdaQuery()
                .select(PointsBoard::getId, PointsBoard::getPoints, PointsBoard::getUserId)
                .page(query.toMpPage(query.getSortBy(),query.getIsAsc()));

        List<PointsBoard> records = boardPage.getRecords();

        if (ObjectUtil.isEmpty(records)) {
            log.error("该赛季无信息");
            return CollUtils.emptyList();
        }

        records.forEach(board -> {
            board.setRank(board.getId().intValue());
            board.setSeason(query.getSeason().intValue());
        });
        return records;
    }

    /**
     * 查询历史赛季我的积分信息 TODO
     *
     * @param season
     * @return
     */
    private PointsBoard queryMyHistoryBoard(Long season) {
        // 1.组装表名
        String tableName = POINTS_BOARD_TABLE_NAME + "_" + season;
        TableNameContext.setTableName(tableName);

        // 2.查询榜单
        PointsBoard board = this.lambdaQuery().select(PointsBoard::getId, PointsBoard::getPoints, PointsBoard::getUserId)
        .eq(PointsBoard::getUserId, UserContext.getUser()).one();


        board.setRank(board.getId().intValue());
        board.setSeason(season.intValue());
        return board;
    }

    /**
     * 查询当前赛季榜单信息
     *
     * @param key
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public List<PointsBoard> queryCurrentBoards(String key, @Min(value = 1, message = "页码不能小于1") Integer pageNo, @Min(value = 1, message = "每页查询数量不能小于1") Integer pageSize) {
        // 1.分页查询Redis榜单（第一页 0-9 ，第二页 10-19）
        int from = (pageNo - 1) * pageSize;
        int end = from + pageSize - 1;
        Set<ZSetOperations.TypedTuple<Object>> tuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, from, end);
        if (ObjectUtil.isEmpty(tuples)) {
            return CollUtils.emptyList();
        }

        // 2.封装list并返回
        // redis中的排名是从0开始，需要+1
        int rank = from + 1;
        List<PointsBoard> list = new ArrayList<>(tuples.size());
        for (ZSetOperations.TypedTuple<Object> tuple : tuples) {
            Long userId = (Long) tuple.getValue();
            Double points = tuple.getScore();
            if (ObjectUtil.isEmpty(userId) || ObjectUtil.isEmpty(points)) {
                continue;
            }
            PointsBoard board = new PointsBoard();
            board.setUserId(userId);
            board.setPoints(points.intValue());
            // 先赋值后自增
            board.setRank(rank++);
            list.add(board);
        }
        return list;
    }

    /**
     * 查询当前赛季
     *
     * @param key
     * @return
     */
    private PointsBoard queryMyCurrentBoard(String key) {
        // 绑定key
        BoundZSetOperations boundZSetOperations = redisTemplate.boundZSetOps(key);

        // 查询我的积分
        Double points = boundZSetOperations.score(UserContext.getUser());

        // 查询我的排名
        Long rank = boundZSetOperations.reverseRank(UserContext.getUser());

        // 封装vo
        PointsBoard pointsBoard = new PointsBoard();
        pointsBoard.setPoints(points == null ? 0 :points.intValue());
        pointsBoard.setRank(rank == null ? 0 : rank.intValue());
        return pointsBoard;
    }
}
